package cn.chiship.sdk.core.util.zxing;

import cn.chiship.sdk.core.base.constants.RegexConstants;
import cn.chiship.sdk.core.exception.custom.BusinessException;
import cn.chiship.sdk.core.text.CharsetKit;
import cn.chiship.sdk.core.util.*;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang3.ObjectUtils;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * 二维码生成和读的工具类
 *
 * @author lijian
 */
public class QrCodeUtil {

	private QrCodeUtil() {

	}

	/**
	 * 生成包含字符串信息的二维码图片
	 * @param content 二维码携带信息
	 * @return byte[]
	 */
	public static byte[] generateQrCode(String content) {
		return generateQrCode(content, null);
	}

	/**
	 * 生成包含字符串信息的二维码图片
	 * @param content 二维码携带信息
	 * @param logoBytes logo
	 * @return byte[]
	 */
	public static byte[] generateQrCode(String content, byte[] logoBytes) {
		return generateQrCode(content, 1200, logoBytes);
	}

	/**
	 * 生成包含字符串信息的二维码图片
	 * @param content 二维码携带信息
	 * @param size 二维码图片大小
	 */
	public static byte[] generateQrCode(String content, int size) {
		return generateQrCode(content, size, null);
	}

	/**
	 * 生成包含字符串信息的二维码图片
	 * @param content 二维码携带信息
	 * @param size 二维码图片大小
	 * @param logoBytes logo
	 */
	public static byte[] generateQrCode(String content, int size, byte[] logoBytes) {
		if (size < 900 || size > 1200) {
			throw new BusinessException("二维码尺寸建议在900~1200之间");
		}
		Map<EncodeHintType, ErrorCorrectionLevel> hintMap = new HashMap<>(8);
		hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
		QRCodeWriter qrCodeWriter = new QRCodeWriter();
		try {
			BitMatrix byteMatrix = qrCodeWriter.encode(
					new String(content.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1),
					BarcodeFormat.QR_CODE, size, size, hintMap);
			int matrixWidth = byteMatrix.getWidth();
			BufferedImage image = new BufferedImage(matrixWidth - 200, matrixWidth - 200, BufferedImage.TYPE_INT_RGB);
			image.createGraphics();
			Graphics2D graphics = (Graphics2D) image.getGraphics();
			graphics.setColor(Color.WHITE);
			graphics.fillRect(0, 0, matrixWidth, matrixWidth);
			graphics.setColor(Color.BLACK);
			for (int i = 0; i < matrixWidth; i++) {
				for (int j = 0; j < matrixWidth; j++) {
					if (byteMatrix.get(i, j)) {
						graphics.fillRect(i - 100, j - 100, 1, 1);
					}
				}
			}
			if (ObjectUtils.isNotEmpty(logoBytes)) {
				ImageInputStream imageInputstream = new MemoryCacheImageInputStream(
						new ByteArrayInputStream(logoBytes));
				BufferedImage logo = ImageIO.read(imageInputstream);
				graphics.drawImage(logo, (matrixWidth - 200) * 2 / 5, (matrixWidth - 200) * 2 / 5, matrixWidth * 2 / 10,
						matrixWidth * 2 / 10, null);
				graphics.dispose();
				logo.flush();
			}
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			ImageIO.write(image, "JPEG", byteArrayOutputStream);
			byteArrayOutputStream.flush();
			byte[] imageInByte = byteArrayOutputStream.toByteArray();
			byteArrayOutputStream.close();
			return imageInByte;
		}
		catch (Exception e) {
			throw new BusinessException("二维码生成出现错误！如：" + e.getLocalizedMessage());
		}
	}

	/**
	 * 读取二维码内容
	 * @param file 文件
	 * @return 二维码内容
	 */
	public static String readQrCode(File file) {
		if (ObjectUtils.isEmpty(file) || file.isDirectory() || !file.exists()) {
			return null;
		}
		try {
			return readQrCode(ImageIO.read(file));
		}
		catch (IOException e) {
			throw new BusinessException("二维码解析错误，原因：" + e.getLocalizedMessage());
		}
		catch (NotFoundException e) {
			throw new BusinessException("图片非二维码图片：" + file.getPath());

		}
	}

	/**
	 * 读取二维码内容
	 * @param is 文件流
	 * @return 结果
	 */
	public static String readQrCode(InputStream is) {
		if (ObjectUtils.isEmpty(is)) {
			return null;
		}
		try {
			return readQrCode(ImageIO.read(is));
		}
		catch (IOException e) {
			throw new BusinessException("二维码解析错误，原因：" + e.getLocalizedMessage());
		}
		catch (NotFoundException e) {
			throw new BusinessException("图片文件流非二维码图片");
		}
	}

	/**
	 * 读取二维码内容
	 * @param bufferedImage
	 * @return 结果
	 * @throws NotFoundException
	 */
	public static String readQrCode(BufferedImage bufferedImage) throws NotFoundException {
		BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(bufferedImage);
		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
		Map<DecodeHintType, Object> hints = new EnumMap(DecodeHintType.class);
		hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
		Result result = new MultiFormatReader().decode(bitmap, hints);
		return result.getText();
	}

	/**
	 * 生成条形码
	 * @param barCode 条码内容
	 * @return 条码字节数组
	 */
	public static byte[] generateBarCode(String barCode) {
		return generateBarCode(barCode, true);
	}

	/**
	 * 生成条形码
	 * @param barCode 条码内容
	 * @param isShowBarCode 是否显示条码内容
	 * @return 条码字节数组
	 */
	public static byte[] generateBarCode(String barCode, Boolean isShowBarCode) {
		return generateBarCode(barCode, 440, 100, isShowBarCode);
	}

	/**
	 * 生成条形码
	 * @param barCode 条码内容
	 * @param width 条码宽度
	 * @param height 条码高度
	 * @param isShowBarCode 是否显示条码内容
	 * @return 条码字节数组
	 */
	public static byte[] generateBarCode(String barCode, int width, int height, Boolean isShowBarCode) {
		if (barCode.length() > 18) {
			throw new BusinessException("建议条码内容长度不能超过18位");
		}
		if (!RegexConstants.check(barCode, RegexConstants.CHARSET_NUMBER)) {
			throw new BusinessException("建议条码内容位字母或数字");
		}
		Map<EncodeHintType, Object> hintMap = new HashMap<>(8);
		hintMap.put(EncodeHintType.CHARACTER_SET, CharsetKit.UTF_8);
		hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		hintMap.put(EncodeHintType.MARGIN, 0);
		try {
			Code128Writer writer = new Code128Writer();
			// 编码内容, 编码类型, 宽度, 高度, 设置参数
			BitMatrix bitMatrix = writer.encode(barCode, BarcodeFormat.CODE_128, width, height, hintMap);
			BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix);
			if (Boolean.TRUE.equals(isShowBarCode)) {
				height = height + 40;
			}
			else {
				height = height + 20;
			}
			BufferedImage outImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

			Graphics2D g2d = outImage.createGraphics();

			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
			Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
			g2d.setStroke(s);
			if (Boolean.TRUE.equals(isShowBarCode)) {
				g2d.fillRect(0, 0, width + 50, height + 100);
				g2d.setColor(Color.BLACK);
				g2d.drawImage(image, 10, 10, image.getWidth() - 20, image.getHeight(), null);
				Color color = new Color(0, 0, 0);
				g2d.setColor(color);
				g2d.setFont(new Font("微软雅黑", Font.PLAIN, 16));
				String str = barCode.replace("", "  ").trim();
				int strWidth = g2d.getFontMetrics().stringWidth(str);
				int wordStartX = (width - strWidth) / 2;
				int wordStartY = height - 20;
				g2d.drawString(str, wordStartX, wordStartY + 10);

			}
			else {
				g2d.fillRect(0, 0, width + 20, height + 20);
				g2d.drawImage(image, 10, 10, image.getWidth() - 20, image.getHeight(), null);
			}
			g2d.dispose();
			outImage.flush();
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			ImageIO.write(outImage, "PNG", byteArrayOutputStream);
			return byteArrayOutputStream.toByteArray();
		}
		catch (Exception e) {
			throw new BusinessException("条形码生成出现错误！如：" + e.getLocalizedMessage());
		}
	}

	public static void main(String[] args) throws IOException {
		byte[] bytes = generateBarCode("qqq");
		FileUtil.writeByteArrayToFile(new File("d:/1.png"), bytes);
		bytes = generateBarCode("abcde1234567890123", false);
		FileUtil.writeByteArrayToFile(new File("d:/4.png"), bytes);
		bytes = generateQrCode("eqeqeq1313eqweqweq");
		FileUtil.writeByteArrayToFile(new File("d:/2.png"), bytes);
		bytes = generateQrCode("eqeqeq1313eqweqweq", 1300,
				ImageUtil.getImgByteByFile("D:\\工作\\智舰\\我的文件\\logo\\有边距\\2.png"));
		FileUtil.writeByteArrayToFile(new File("d:/3.png"), bytes);
	}

}
